# -*- coding: utf-8 -*-
import datetime

import pandas as pd
from matplotlib import pyplot as plt
from sympy.physics.units import amount
import datetime
from database.database import etf_data, etf_data_download, eft_rt_data
from strategy import SignGeneration
from tqdm import tqdm
import numpy as np
from pathlib import Path
import time
import os


class Backtesting(object):
    """
    ####################################################################################################
         function : 回测类，主要用户数据回测
         return   : None
         ----------------------------------------------------------------------------------------
         history :
         huangkunyang : create   '2025-02-10'      <519462724@qq.com>
         huangkunyang : update   '2025-06-14'      <519462724@qq.com>
    ####################################################################################################
    """
    def __init__(self, fund=0, fee=0.0001, flower=0.0, debuginfo=False, stop_loss_thd=0.01, operate_rate=1,
                 lowest_sell_thd = 0.0):
        self.buy_sign = False   # 买信号
        self.sell_sign = False
        self.signal = None
        self.code = 0
        self.operate_rate = operate_rate  # 每次操作买卖的百分比
        self.stamp_rate = flower  # 印花税百分比
        self.fee_rate = fee       # 佣金百分比
        self.init_fund = fund  # 初始资产
        self.fund = fund     # 资产
        self.hold_df = pd.DataFrame(columns=["price", "amount"])   # 持仓列表
        self.simulation_dataframe = pd.DataFrame()  # 模拟交易原始数据
        self.fund_history_df = pd.DataFrame()   # 历史资产分析表
        self.debuginfo = debuginfo    # debug 模式
        self.stop_loss_thd = 1 - stop_loss_thd   # 止损百分比
        self.sign_df = pd.DataFrame()    # 买卖信号列表
        self.lowest_sell_thd = lowest_sell_thd   # 最低卖出价格 默认是0, 就是多原价* 1.001才卖

    def init_simulation_dataframe(self, data, mode="minute"):
        if mode == "tick":
            print("=====tick级 数据回测初始化=====")
            # 获取回测需要的基础信息
            self.simulation_dataframe = data.copy()
            self.simulation_dataframe = self.simulation_dataframe[self.simulation_dataframe["成交价"] != 0]
            self.simulation_dataframe["time"] = pd.to_datetime(self.simulation_dataframe['日期'] + ' ' + self.simulation_dataframe['时间'])
            self.simulation_dataframe = self.simulation_dataframe.reset_index()
            print("=====tick级 数据回测初始完成=====")
            print()
        elif mode == "minute":
            print("=====分钟级 数据回测初始化=====")
            # 获取回测需要的基础信息
            self.simulation_dataframe = data.copy()
            # self.simulation_dataframe.rename(columns={"open": "成交价", "date":"time"}, inplace=True)
            self.simulation_dataframe["成交价"] = self.simulation_dataframe["open"]
            self.simulation_dataframe["time"] = self.simulation_dataframe["date"]
            self.simulation_dataframe = self.simulation_dataframe.reset_index()
            print("=====分钟级 数据回测初始完成=====")
        else:
            raise RuntimeError("mode 模式选取错误，请从 [minute, tick] 选取")

    def get_simulation_dataframe(self):
        return self.simulation_dataframe

    def split_day_data(self):
        return self.simulation_dataframe.groupby("日期")

    def update_signal(self, price):
        """
        ####################################################################################################
             function : 更新买卖信号函数
             return   : None
             ---------------------------------------parameter ---------------------------------------
             price :  当前价格
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        if isinstance(self.sign_df, pd.DataFrame):
            # print(self.sign_df.loc[self.sign_df.index[-1:], ["Buy_Signal", "Sell_Signal"]])
            # 当没有买卖信号时等待
            if (self.sign_df.loc[self.sign_df.index[-1], "Buy_Signal"] == 0 and
                    self.sign_df.loc[self.sign_df.index[-1], "Sell_Signal"] == 0):
                self.buy_sign = False
                self.sell_sign = False
                return None
            # 当出现买信号时
            elif self.sign_df.loc[self.sign_df.index[-1], "Buy_Signal"] != 0:
                self.buy_sign = True
                self.sell_sign = False
                return "buy"
            # 当出现卖信号时
            elif self.sign_df.loc[self.sign_df.index[-1], "Sell_Signal"] != 0:
                self.buy_sign = False
                # 当有持仓时
                if not self.fund_history_df.empty:
                    # 当价格低于最低售卖价格时，不进行卖出
                    if price < self.fund_history_df.loc[self.fund_history_df.index[-1], "base_price"] * self.lowest_sell_thd:
                        self.sell_sign = False
                        return None
                    else:
                        self.sell_sign = True
                        return "sell"
                else:
                    return None
            else:
                raise Exception("触发信号错误")
        else:
            return None

    def update_signal_by_index(self, price, signal_index):
        """
        ####################################################################################################
             function : 按指定行数的数据，去更新买卖信号函数，而不是每次取最后一行
             return   : None
             ---------------------------------------parameter ---------------------------------------
             price :  当前价格
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        if isinstance(self.sign_df, pd.DataFrame):
            # print(self.sign_df.loc[self.sign_df.index[signal_index], ["Buy_Signal", "Sell_Signal"]])
            # 当没有买卖信号时等待
            if (self.sign_df.loc[self.sign_df.index[signal_index], "Buy_Signal"] == 0 and
                    self.sign_df.loc[self.sign_df.index[signal_index], "Sell_Signal"] == 0):
                self.buy_sign = False
                self.sell_sign = False
                return None
            # 当出现买信号时
            elif self.sign_df.loc[self.sign_df.index[signal_index], "Buy_Signal"] != 0:
                self.buy_sign = True
                self.sell_sign = False
                return "buy"
            # 当出现卖信号时
            elif self.sign_df.loc[self.sign_df.index[signal_index], "Sell_Signal"] != 0:
                self.buy_sign = False
                # 当有持仓时
                if not self.fund_history_df.empty:
                    # 当价格低于最低售卖价格时，不进行卖出
                    if price < self.fund_history_df.loc[self.fund_history_df.index[-1], "base_price"] * self.lowest_sell_thd:
                        self.sell_sign = False
                        return None
                    else:
                        self.sell_sign = True
                        return "sell"
                else:
                    return None
            else:
                raise Exception("触发信号错误")
        else:
            return None

    def backtesting(self, backtesting_type="rt", strategy_name="KDJ", union_weight=None):
        """
        ####################################################################################################
             function : 回测主函数，调用用于回测特定时间段内的策略结果
             return   : None
             ---------------------------------------parameter ---------------------------------------
             backtesting_type : 回测模式【rt 与 whole】
             strategy_name : 回测策略 【KDJ， RSI， MACD， UNION】
             union_weight :  当调用模式为UNION时，可以对联合模型中所有模型进行权重修改
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        # ======= 定义参数 =======
        # backtesting_type = "rt"    # 采用回测模式  rt:实时回测， whole: 整体回测(目前不完善)
        sign_genrator = SignGeneration()
        # =======================
        # 当回测数据不为空时
        if not self.simulation_dataframe.empty:
            code = self.simulation_dataframe["code"][0]
            self.code = code
            # 整体回测，未完善
            if backtesting_type == "whole":
                print("======整体回测 %s=======" % code)
                self.sign_df = sign_genrator.generate_sign(code, self.simulation_dataframe)
                for info_index in tqdm(self.sign_df.index):
                    if self.sign_df.loc[info_index, "Buy_Signal"] == 0 and self.sign_df.loc[info_index, "Sell_Signal"] == 0:
                        self.buy_sign = False
                        self.sell_sign = False
                        continue
                    elif self.sign_df.loc[info_index, "Buy_Signal"] != 0:
                        self.buy_sign = True
                        self.sell_sign = False
                    elif self.sign_df.loc[info_index, "Sell_Signal"] != 0:
                        self.buy_sign = False
                        self.sell_sign = True
                    else:
                        raise Exception("触发信号错误")

                    current_seires = self.sign_df.loc[info_index, :]
                    self.trade_module(code, current_seires)
                self.show_assert()

            # 实时回测
            elif backtesting_type == "rt":
                print("======开始分时回测 %s=======" % code)
                # 将回测数据，生成一个按天分类的生成器（待开发）
                day_generator = self.split_day_data()
                # 对每天进行回测
                trade_open_time = pd.to_datetime("09:40:00").time()  # 开始交易时间
                trade_close_time = pd.to_datetime("14:50:00").time()  # 结束交易时间
                for day, day_df in day_generator:
                    # ========= 定义输出日志统计变量 ====================
                    b_cnt = 0       # 初始化每天买计数
                    s_cnt = 0       # 初始化每天卖计数
                    loss_cnt = 0    # 初始化每天止损计数
                    clear_cnt = 0   # 初始化每天清仓计数
                    # ==============================================
                    start = datetime.datetime.now()
                    sign_genrator.clean_history()   # 清理昨天的数据
                    # 对每条数据一次进行预测
                    for info_index in day_df.index:
                        current_series = day_df.loc[info_index, :]  # 获取当前时间数据
                        time = pd.to_datetime(current_series["date"]).time()  # 获取当前时间

                        # 当在交易时间以内时
                        if (time > trade_open_time) & (time < trade_close_time):
                            # 生成买卖信号
                            self.sign_df = sign_genrator.generate_sign_rt(code, current_series, strategy_name, union_weight)
                            signal = self.update_signal(price=current_series["成交价"])
                            # print(signal)
                            # print("=================")
                            # 监控是否有交易信号
                            if signal is None:
                                # 如果有持仓，则持续监控价格
                                if not self.hold_df.empty:
                                    hold_code = self.hold_df.iloc[0, :]
                                    # 如果低于止损线则触发止损
                                    if hold_code["price"] < current_series["成交价"] * self.stop_loss_thd:
                                        self.operate(current_series, "stop_loss")
                                        loss_cnt += 1
                                    else:
                                        continue
                                else:
                                    continue
                            # 如果有信号就执行买卖
                            else:
                                amount = self.trade_module(signal, current_series)
                                if (signal == "buy") & (amount != 0):
                                    b_cnt += 1
                                # 当触发卖信号时
                                if (signal == "sell") & (amount != 0):
                                    s_cnt += 1
                                # self.trade_module(signal, current_series)

                        # 超过交易时间就清仓
                        if time > trade_close_time:
                            if code in self.hold_df.index.to_list():
                                # 获取当前code的持仓信息
                                current_seires = day_df.loc[info_index, :]
                                self.operate(current_seires, "sell_clear")
                                clear_cnt += 1
                    end = datetime.datetime.now()
                    duration = (end - start).seconds + (end - start).microseconds / 1000 / 1000
                    print(">>> %s 回测 >>：  当天执行买|卖次数  >> %d|%d <<  执行止损清仓 %d 次  >> 尾盘清仓 %d 次， 回测用时%.2f s" % (day, b_cnt, s_cnt, loss_cnt, clear_cnt, duration))

                # print(self.sign_df)
                self.show_assert()

            # 天级回测一次性算完
            elif backtesting_type == "rt_day":
                print("======开始天级分时回测 %s=======" % code)
                # 将回测数据，生成一个按天分类的生成器（待开发）
                day_generator = self.split_day_data()
                # 对每天进行回测
                trade_open_time = pd.to_datetime("09:40:00").time()  # 开始交易时间
                trade_close_time = pd.to_datetime("14:50:00").time()  # 结束交易时间
                for day, day_df in day_generator:
                    # ========= 定义输出日志统计变量 ====================
                    b_cnt = 0       # 初始化每天买计数
                    s_cnt = 0       # 初始化每天卖计数
                    loss_cnt = 0    # 初始化每天止损计数
                    clear_cnt = 0   # 初始化每天清仓计数
                    # ==============================================
                    start = datetime.datetime.now()
                    sign_genrator.clean_history()   # 清理昨天的数据
                    # print("================= day =================")
                    # print(day)
                    day_df.reset_index(inplace=True)
                    # print("================= day_df =================")
                    # print(day_df)

                    # 生成买卖信号
                    self.sign_df = sign_genrator.generate_sign_rt(code, day_df, strategy_name, union_weight)
                    # 对每条数据一次进行预测
                    for info_index in day_df.index:
                        # print("================= info_index =================")
                        # print(info_index)
                        current_series = day_df.loc[info_index, :]  # 获取当前时间数据
                        # print("================= current_series =================")
                        # print(current_series)
                        time = pd.to_datetime(current_series["date"]).time()  # 获取当前时间
                        # 当在交易时间以内时
                        if (time > trade_open_time) & (time < trade_close_time):
                            signal = self.update_signal_by_index(price=current_series["成交价"], signal_index=info_index)
                            # print("================= signal =================")
                            # print(signal)
                            # 监控是否有交易信号
                            if signal is None:
                                # 如果有持仓，则持续监控价格
                                if not self.hold_df.empty:
                                    hold_code = self.hold_df.iloc[0, :]
                                    # 如果低于止损线则触发止损
                                    if hold_code["price"] < current_series["成交价"] * self.stop_loss_thd:
                                        self.operate(current_series, "stop_loss")
                                        loss_cnt += 1
                                    else:
                                        continue
                                else:
                                    continue
                            # 如果有信号就执行买卖
                            else:
                                amount = self.trade_module(signal, current_series)
                                if (signal == "buy") & (amount != 0):
                                    b_cnt += 1
                                # 当触发卖信号时
                                if (signal == "sell") & (amount != 0):
                                    s_cnt += 1
                                # self.trade_module(signal, current_series)

                        # 超过交易时间就清仓
                        if time > trade_close_time:
                            if code in self.hold_df.index.to_list():
                                # 获取当前code的持仓信息
                                current_seires = day_df.loc[info_index, :]
                                self.operate(current_seires, "sell_clear")
                                clear_cnt += 1
                    end = datetime.datetime.now()
                    duration = (end - start).seconds + (end - start).microseconds / 1000 / 1000
                    print(">>> %s 回测 >>：  当天执行买|卖次数  >> %d|%d <<  执行止损清仓 %d 次  >> 尾盘清仓 %d 次， 回测用时%.2f s" % (day, b_cnt, s_cnt, loss_cnt, clear_cnt, duration))

                # print(self.sign_df)
                self.show_assert()

    def backtesting_sim(self, code, current_series, sign_genrator, strategy_name="UNION", union_weight=None):
        """
        ####################################################################################################
             function : 回测主函数，调用用于回测特定时间段内的策略结果
             return   : None
             ---------------------------------------parameter ---------------------------------------
             backtesting_type : 回测模式【rt 与 whole】
             strategy_name : 回测策略 【KDJ， RSI， MACD， UNION】
             union_weight :  当调用模式为UNION时，可以对联合模型中所有模型进行权重修改
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        # ======= 定义参数 =======
        # backtesting_type = "rt"    # 采用回测模式  rt:实时回测， whole: 整体回测(目前不完善)
        # =======================

        # 实盘回测
        print("====== %s=======" % code)
        # 对每天进行回测
        trade_open_time = pd.to_datetime("09:40:00") - pd.Timedelta(days=1)  # 开始交易时间
        trade_close_time = pd.to_datetime("14:50:00")  # 结束交易时间
        # ========= 定义输出日志统计变量 ====================
        b_cnt = 0  # 初始化每天买计数
        s_cnt = 0  # 初始化每天卖计数
        loss_cnt = 0  # 初始化每天止损计数
        clear_cnt = 0  # 初始化每天清仓计数
        # ==============================================
        start = datetime.datetime.now()
        # sign_genrator.clean_history()  # 清理昨天的数据
        # 对每条数据一次进行预测
        current_time = current_series["date"]  # 获取当前时间
        current_time = current_time + pd.Timedelta(hours=8)
        print("当前时间", current_time, "开始时间", trade_open_time, "---- 结束时间", trade_close_time)

        # 当在交易时间以内时
        if (current_time > trade_open_time) & (current_time < trade_close_time):
            # 生成买卖信号
            self.sign_df = sign_genrator.generate_sign_rt(code, current_series, strategy_name, union_weight)
            # print(self.sign_df)
            signal = self.update_signal(price=current_series["成交价"])
            # 监控是否有交易信号
            if signal is None:
                # 如果有持仓，则持续监控价格
                if not self.hold_df.empty:
                    hold_code = self.hold_df.iloc[0, :]
                    # 如果低于止损线则触发止损
                    if hold_code["price"] < current_series["成交价"] * self.stop_loss_thd:
                        self.operate(current_series, "stop_loss")
                        loss_cnt += 1
            # 如果有信号就执行买卖
            else:
                amount = self.trade_module(signal, current_series)
                if (signal == "buy") & (amount != 0):
                    b_cnt += 1
                # 当触发卖信号时
                if (signal == "sell") & (amount != 0):
                    s_cnt += 1
                # self.trade_module(signal, current_series)

        # 超过交易时间就清仓
        if current_time > trade_close_time:
            if code in self.hold_df.index.to_list():
                # 获取当前code的持仓信息
                self.operate(current_series, "sell_clear")
                clear_cnt += 1
        end = datetime.datetime.now()
        duration = (end - start).seconds + (end - start).microseconds / 1000 / 1000
        print(
            ">>> 回测 >>：  当天执行买|卖次数  >> %d|%d <<  执行止损清仓 %d 次  >> 尾盘清仓 %d 次， 回测用时%.2f s" % (
            b_cnt, s_cnt, loss_cnt, clear_cnt, duration))

        # print(self.sign_df)
        if not self.fund_history_df.empty:
            self.show_assert()

    def update_history_df(self, code, __type, current_seires, __amount, hold_asset):
        """
        ####################################################################################################
             function : 更新历史dataframe
             return   : None
             ---------------------------------------parameter ---------------------------------------
             code :  执行操作的etf代码
             __type :  操作类型
             current_seires : 当前的操作的实时数据
             __amount :  操作的量
             hold_asset :  持仓资产
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        self.fund_history_df = self.fund_history_df._append(
            {"time": current_seires["time"],  # 交易时间
             "base_price": current_seires["成交价"],  # 交易成本
             "type": __type,  # 交易类型
             "fund": self.fund,  # 现金
             "amount": __amount,  # 交易量
             "fee": __amount * current_seires["成交价"] * self.fee_rate,  # 佣金
             "stamp": 0 if __type == "buy" else __amount * current_seires["成交价"] * self.stamp_rate,
             "hold_asset": hold_asset,  # 持仓资产
             "total_asset": self.fund + hold_asset}, ignore_index=True)  # 总资产
        if self.debuginfo:
            print("{} :: 本次".format(current_seires["time"]), end="")
            print("操作 [ ", __type, " ]", end="")
            print(" {} 量 {}，价格为{}， 合计{}, 交易费用{} ,当前资产： >>{} <<".format(
                code, __amount,
                current_seires["成交价"],
                __amount * current_seires["成交价"],
                __amount * current_seires["成交价"] * self.fee_rate,
                self.fund + hold_asset))

    def operate(self, current_seires, type):
        """
        ####################################################################################################
             function : 操作函数，根据输入类型执行不同操作
             return   : None
             ---------------------------------------parameter ---------------------------------------
             current_seires : 操作当前tick的实时数据
             type :   执行的各类操作
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        # 执行操作更新数据 self.hold_df, self.fund
        __type = type
        code = current_seires["code"]
        if __type == "buy":
            # 计算可买入数量
            __amount = self.fund / (current_seires["成交价"] * (1 + self.fee_rate)) // 100 * 100
            __amount = __amount * self.operate_rate  # 计算实际需要买入数量
            # 更新资金
            self.fund -= __amount * current_seires["成交价"] + __amount * current_seires["成交价"] * self.fee_rate
            # ----- 更新持仓信息 -------
            # 新增持仓
            if code not in self.hold_df.index:
                self.hold_df.loc[code, "amount"] = __amount
                self.hold_df.loc[code, "price"] = current_seires["成交价"]
            # 更新持仓信息
            else:
                self.hold_df.loc[code, "price"] = (self.hold_df.loc[code, "amount"] * self.hold_df.loc[code, "price"]
                + current_seires["成交价"] * __amount) / (self.hold_df.loc[code, "amount"] + __amount)
                self.hold_df.loc[code, "amount"] += __amount

        elif __type == "sell":
            # 持仓的code 中有需要卖出的code
            if code in self.hold_df.index.to_list():
                # 获取当前code的持仓信息
                temp_series = self.hold_df.loc[code, :]
                # 单次操作卖出仓位设置
                __amount = temp_series["amount"] * self.operate_rate
                # 更新持仓信息
                self.hold_df.loc[code, "amount"] = temp_series["amount"] - __amount
                # 更新资金
                self.fund += __amount * current_seires["成交价"] - __amount * current_seires[
                    "成交价"] * self.fee_rate - __amount * current_seires["成交价"] * self.stamp_rate
            else:
                __amount = 0

        elif __type == "stop_loss":
            # 持仓的code 中有需要卖出的code
            if code in self.hold_df.index.to_list():
                # 获取当前code的持仓信息
                temp_series = self.hold_df.loc[code, :]
                # 单次操作卖出仓位设置
                __amount = temp_series["amount"]
                # 更新持仓信息
                self.hold_df.loc[code, "amount"] = temp_series["amount"] - __amount
                # 更新资金
                self.fund += __amount * current_seires["成交价"] - __amount * current_seires[
                    "成交价"] * self.fee_rate - __amount * current_seires["成交价"] * self.stamp_rate
            else:
                __amount = 0

        elif __type == "sell_clear":
            # 持仓的code 中有需要卖出的code
            if code in self.hold_df.index.to_list():
                # 获取当前code的持仓信息
                temp_series = self.hold_df.loc[code, :]
                # 单次操作卖出仓位设置
                __amount = temp_series["amount"]
                # 更新持仓信息
                self.hold_df.loc[code, "amount"] = temp_series["amount"] - __amount
                # 更新资金
                self.fund += __amount * current_seires["成交价"] - __amount * current_seires[
                    "成交价"] * self.fee_rate - __amount * current_seires["成交价"] * self.stamp_rate
            else:
                __amount = 0
        else:
            raise RuntimeError("操作类型错误")
        # 更新内容
        if __amount != 0:
            # 计算总持仓价值
            hold_asset = 0
            for code_sell, series in self.hold_df.iterrows():
                hold_asset += series["amount"] * current_seires["成交价"]
            # 更新持仓列表
            self.hold_df = self.hold_df[self.hold_df["amount"] != 0]
            # 更新历史交易记录
            self.update_history_df(code, __type, current_seires, __amount, hold_asset)
        return __amount

    def trade_module(self, signal, current_seires):
        # 当触发买信号时，进行完全买入操作
        if signal == "buy":
            # 买入数量等于 self.fund / 价格 以100向下取整（计算时考虑交税）
            # print("===========")
            # print(current_seires)
            __amount = int(self.fund / current_seires["成交价"])
            __amount -= __amount * self.fee_rate
            __amount = __amount// 100 * 100

            # 当买入量大于 0 时，执行买操作
            if __amount > 0:
                self.operate(current_seires, type="buy")
            return __amount
        # 当触发卖信号时
        elif signal == "sell":
            __amount = self.operate(current_seires, type="sell")
            return __amount

        else:
            raise RuntimeError("信号错误")


    def show_assert(self):
        """
        ####################################################################################################
             function : 打印持有资金
             return   : None
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        print("")
        print("")
        print("============  回测记录  ===============")
        lastest_assert = self.fund_history_df.iloc[-1, :]
        print("资金总量为：%.2f" % lastest_assert["fund"], end="----")
        print("持有资产为：%.2f" % lastest_assert["hold_asset"], end="---||  ")
        print("总资产为：%.2f" % lastest_assert["total_asset"])
        print("***********************************")

    def save_file(self, df, name):
        """
        ####################################################################################################
             function : 保存文件函数，如果出现无法保存的情况，自动编号+1保存
             return   : None
             ---------------------------------------parameter ---------------------------------------
             df :   需要保存的dataframe
             name :    需要保存文件的名称
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        i = "0"
        while True:
            try:
                if i == "0":
                    file_name = name
                    # 如果文件夹不存在，则需要创建
                    os.makedirs(os.path.dirname(file_name), exist_ok=True)
                    # 文件不存在也要创建
                    if not Path(file_name).exists():
                        with open(file_name, 'w') as f:
                            f.write("")  # 写入空内容或初始化数据
                        print(f"文件 {file_name} 已创建")
                    df.to_csv(file_name)
                    print("file has been successfully write to %s" % name)
                    break
                else:
                    file_name = name[:-4] + "_" + str(i) + name[-4:]
                    # 如果文件夹不存在，则需要创建
                    os.makedirs(os.path.dirname(file_name), exist_ok=True)
                    # 文件不存在也要创建
                    if not Path(file_name).exists():
                        with open(file_name, 'w') as f:
                            f.write("")  # 写入空内容或初始化数据
                        print(f"文件 {file_name} 已创建")
                    df.to_csv(file_name)
                    print("file has been successfully write to %s" % name)
                    break
            except Exception as E:
                print("save_file 抛异常：")
                print(E)
                time.sleep(0.5)
                i = str(int(i) + 1)


    def plot_result(self):
        """
        ####################################################################################################
             function : 打印图像函数
             return   : None
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        fig = plt.figure(figsize=(12, 6))
        ax = fig.add_subplot(111)
        self.fund_history_df["total_asset_percent"] = (self.fund_history_df["total_asset"] - self.fund_history_df["total_asset"].iloc[0]) / self.fund_history_df["total_asset"].iloc[0]
        self.fund_history_df["base_price_percent"] = (self.fund_history_df["base_price"] - self.fund_history_df["base_price"].iloc[0]) / self.fund_history_df["base_price"].iloc[0]
        # print(self.fund_history_df.head(50))
        self.save_file(self.fund_history_df, "./log/" + "fund_history.csv")
        self.save_file(self.sign_df, "./log/" + "sign_df.csv")

        print("佣金：", self.fund_history_df["fee"].sum())
        print("印花税: ", self.fund_history_df["stamp"].fillna(0).sum())

        # 绘制资金曲线
        ax.plot(self.fund_history_df.index, self.fund_history_df["total_asset_percent"], label="total_asset")
        # 绘制收益基线
        ax.plot(self.fund_history_df.index, self.fund_history_df["base_price_percent"], label="base_price")

        x_tick_df = self.fund_history_df.copy()
        x_tick_df["day"] = x_tick_df["time"].str[2:10]
        x_tick = x_tick_df.drop_duplicates(subset="day", keep="first")
        if x_tick.shape[0] < 40:
            ax.set_xticks(x_tick.index)
            ax.set_xticklabels(x_tick["day"], rotation=75)
        else:
            gap = (x_tick.shape[0] // 40)+ 1
            ax.set_xticks(x_tick.index[::gap])
            ax.set_xticklabels(x_tick["day"][::gap], rotation=75)
        ax.legend()
        plt.show()

    def generate_report(self):
        """
        ####################################################################################################
             function : 打印各类统计结果
             return   : None
             ----------------------------------------------------------------------------------------
             history :
             huangkunyang : create   '2025-07-09'      <519462724@qq.com>
        ####################################################################################################
        """
        print("")
        print("")
        print("============  指标计算 ===============")
        __df = self.fund_history_df.copy()
        __df['time'] = pd.to_datetime(__df['time'])
        __df = __df.sort_values('time')

        # 计算基本指标
        total_buy = len(__df[__df['type'] == 'buy'])
        total_sell = len(__df[__df['type'] == 'sell'])
        total_sell_clear = len(__df[__df['type'] == 'sell_clear'])
        total_fee = __df['fee'].sum()
        total_stamp = __df['stamp'].sum()

        # 计算成功率
        success = 0
        failure = 0
        invalid = 0
        trade_turnover = 0
        last_buy_price = None

        for index, row in __df.iterrows():
            if row['type'] == 'buy':
                last_buy_price = row['base_price']
            elif row['type'] == 'sell' and last_buy_price is not None:
                if row['base_price'] > last_buy_price:
                    success += 1
                elif row['base_price'] < last_buy_price:
                    failure += 1
                else:
                    invalid += 1
            trade_turnover += row['base_price'] + row['amount']

        success_rate = success / (total_sell + total_sell_clear) if total_sell > 0 else 0

        # 计算 年交易倍数
        trade_turnover_rate = trade_turnover / self.init_fund if self.init_fund > 0 else 0

        # 计算收益率
        initial_asset = __df.iloc[0]['total_asset']
        final_asset = __df.iloc[-1]['total_asset']
        cumulative_return = (final_asset - initial_asset) / initial_asset

        # 计算年化收益率
        start_date = __df.iloc[0]['time']
        end_date = __df.iloc[-1]['time']
        days = (end_date - start_date).days
        years = days / 365.25
        annualized_return = (1 + cumulative_return) ** (1 / years) - 1 if years > 0 else 0

        # 计算年化波动率
        daily_returns = __df['total_asset'].pct_change().dropna()
        annualized_volatility = daily_returns.std() * np.sqrt(252)  # 假设252个交易日

        # 计算夏普比率 (无风险利率设为1.1%)
        risk_free_rate = 0.011
        sharpe_ratio = (annualized_return - risk_free_rate) / annualized_volatility if annualized_volatility > 0 else 0

        # 计算最大回撤
        peak = __df['total_asset'].cummax()
        drawdown = (__df['total_asset'] - peak) / peak
        max_drawdown = drawdown.min()

        # 计算卡玛比率
        calmar_ratio = annualized_return / abs(max_drawdown) if max_drawdown != 0 else 0

        # 计算alpha和beta (这里简化处理，实际需要基准数据)
        alpha = np.nan
        beta = np.nan

        # 输出结果
        print("回测时间:", start_date, "至", end_date)
        print("正常清仓次数:", total_sell)
        print("尾盘清仓次数:", total_sell_clear)
        print("总计清仓次数:", total_sell + total_sell_clear)
        print("成功率: {:.2%}".format(success_rate))
        print("总计佣金: ", total_fee)
        print("总计印花税: ", total_stamp)
        print("累计收益率: {:.2%}".format(cumulative_return))
        print("年化收益率: {:.2%}".format(annualized_return))
        print("年化波动率: {:.2%}".format(annualized_volatility))
        print("夏普比率(无风险利率1.1%): {:.2f}".format(sharpe_ratio))
        print("最大回撤: {:.2%}".format(max_drawdown))
        print("卡玛比率: {:.2f}".format(calmar_ratio))
        print("Alpha系数:", alpha)
        print("Beta系数:", beta)

if __name__ == '__main__':
    # data = etf_data_download()
    # df = data.load_data(code="513180", start_date="20240801", end_date="20240802")
    data = eft_rt_data()
    try:
        df = data.load_data(code=["513130"], start_date="20240201", end_date="20240230")
    except RuntimeError:
        print("数据库没有相应数据,请修改data.load_data函数的code、start_date和end_date参数")
        exit()

    backtest = Backtesting(fund=10000000, debuginfo=False, lowest_sell_thd=0)

    backtest.init_simulation_dataframe(df)
    backtest.backtesting(backtesting_type="rt", strategy_name="UNION")

    backtest.generate_report()
    backtest.plot_result()